/*
 *
 *
Copyright (c) 1998-2001 Aristar, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:

"This product includes software developed by Aristar, Inc. (http://www.aristar.com/)."

Alternately, this acknowledgment may appear in the software itself, if and
wherever such third-party acknowledgments normally appear.

4. The names "Dialect" and "Aristar, Inc." must not be used to endorse or
promote products derived from this software without prior written permission.
For written permission, please contact info@aristar.com.

5. Products derived from this software may not be called "Dialect", nor may
"Dialect" appear in their name, without prior written permission of Aristar, Inc.

THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
ARISTAR, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

This software consists of voluntary contributions made by many individuals
on behalf of Aristar, Inc.  For more information on Aristar, Inc.,
please see <http://www.aristar.com/>.

Portions of this software are based upon software freely available and
written by individuals not associated with Aristar, Inc.  Copyrights are
maintained in the relevant source code files.
 *
 *
 */

// DialectView.cpp : implementation of the CDialectView class
//

#include "stdafx.h"
#include "DialectApp.h"


#ifdef IDE_SUPPORT


#include "MainFrm.h"
#include "DialectView.h"
#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#ifdef __MDI__
#	define SET_TITLE(x)				\
		{							\
		m_strTitle = x;				\
		SetWindowText(x);			\
		OnUpdateFrameTitle(TRUE);	\
		}
#else
#	define SET_TITLE(x) SetWindowText(x)
#endif

extern void SplitFullPath(CString input, CString& directory, CString& filename);

/////////////////////////////////////////////////////////////////////////////
// CDialectView

IMPLEMENT_DYNCREATE(CDialectView, BASEVIEWWND)

BEGIN_MESSAGE_MAP(CDialectView, BASEVIEWWND)
	//{{AFX_MSG_MAP(CDialectView)
	ON_WM_SETFOCUS()
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_WM_CLOSE()
	ON_WM_DESTROY()
	ON_WM_ACTIVATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDialectView construction/destruction

CDialectView::CDialectView()
{
	// TODO: add member initialization code here

}

CDialectView::~CDialectView()
{
}

BOOL CDialectView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: Modify the Window class or styles here by modifying
	//  the CREATESTRUCT cs

	if( !BASEVIEWWND::PreCreateWindow(cs) )
		return FALSE;

	cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
	cs.lpszClass = AfxRegisterWndClass(0);

	return TRUE;
}



/////////////////////////////////////////////////////////////////////////////
// CDialectView diagnostics

#ifdef _DEBUG
void CDialectView::AssertValid() const
{
	BASEVIEWWND::AssertValid();
}

void CDialectView::Dump(CDumpContext& dc) const
{
	BASEVIEWWND::Dump(dc);
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CDialectView message handlers
/*void CDialectView::OnFileClose()
{
	// To close the frame, just send a WM_CLOSE, which is the equivalent
	// choosing close from the system menu.

	SendMessage(WM_CLOSE);
}*/

int CDialectView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (BASEVIEWWND::OnCreate(lpCreateStruct) == -1)
		return -1;

	// create a view to occupy the client area of the frame
	//if (!m_CodeEditWnd.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW,
	//	CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL))
	if (!m_CodeEditWnd.Create(AFX_WS_DEFAULT_VIEW,
		CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST))
	{
		TRACE0("Failed to create view window\n");
		return -1;
	}

	// Set a default window title (used as module name when nothing better)
	static int count = 1;
	m_TempName.Format(_T("Untitled%d"), count++);
	SET_TITLE(m_TempName);

	return 0;
}

void CDialectView::OnSetFocus(CWnd* pOldWnd)
{
	CWnd::OnSetFocus(pOldWnd);
	if (m_CodeEditWnd.GetSafeHwnd() != NULL) {
		m_CodeEditWnd.SetFocus();
	}
}

bool CDialectView::GetShowLineNumbers()
{
	return m_CodeEditWnd.GetShowLineNumbers();
}

void CDialectView::SetShowLineNumbers(bool bShow)
{
	m_CodeEditWnd.SetShowLineNumbers(bShow);
}

BOOL CDialectView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
	// let the view have first crack at the command
	if (m_CodeEditWnd.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
		return TRUE;

	// otherwise, do default handling
	return BASEVIEWWND::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}


void CDialectView::OnSize(UINT nType, int cx, int cy)
{
	BASEVIEWWND::OnSize(nType, cx, cy);
	if (::IsWindow(m_CodeEditWnd.m_hWnd)) {
		m_CodeEditWnd.SetWindowPos(NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER);
	}
}

void CDialectView::FlashLine(int line, COLORREF color)
{
	m_CodeEditWnd.FlashLine(line, color);
}

CString CDialectView::GetText()
{
	return m_CodeEditWnd.GetText();
}

void CDialectView::SetText(CString text)
{
	m_CodeEditWnd.SetText(text);
}

CString CDialectView::GetModuleName()
{
	CString mod = m_Filename;
	if (mod.IsEmpty()) {
		mod = m_TempName;
	}
	else if (mod.Right(2).CompareNoCase(_T(".d")) == 0) {
		mod = mod.Left(mod.GetLength() - 2);
	}
	return mod;
}

CString CDialectView::GetFilename()
{
	return m_Directory + m_Filename;
}

void CDialectView::SetFileInfo(CString directory, CString filename)
{
	m_Filename = filename;
	m_Directory = directory;
	SET_TITLE(filename);
}

void CDialectView::GetFileInfo(CString& directory, CString& filename)
{
	filename = m_Filename;
	directory = m_Directory;
}

void CDialectView::EditModified(bool bModified)
{
	CString name = m_Filename;
	if (name.IsEmpty()) name = m_TempName;

	if (bModified) {
		SET_TITLE(name + _T("*"));
	}
	else {
		SET_TITLE(name);
	}
}

void CDialectView::OnClose()
{
	if (m_CodeEditWnd.GetModified()) {
		if (!Save(true)) return;	// User pressed cancel
	}
	BASEVIEWWND::OnClose();
}

void CDialectView::OnDestroy()
{
	// Remove quick references...
	g_ModNameToView.Set(GetModuleName(), AllocNil());
	if (!m_Filename.IsEmpty()) {
		g_FilenameToView.Set(m_Directory + m_Filename, AllocNil());
	}

	BASEVIEWWND::OnDestroy();
}

bool CDialectView::Open(CString directory, CString filename)
{
	CString code;
	if (FileToText(directory + filename, code)) {
		SetFileInfo(directory, filename);
		SetText(code);
		g_ModNameToView.Set(GetModuleName(), (int)this);
		g_FilenameToView.Set(directory + filename, (int)this);
		m_CodeEditWnd.SetModified(false);
		return true;
	}
	return false;
}

bool CDialectView::Save(bool bPrompt)
{
	// We will only save if modified
	if (m_CodeEditWnd.GetModified()) {
		if (bPrompt) {
			CString prompt;
			if (m_Filename.IsEmpty()) {
				prompt.Format(_T("Would you like to save '%s'?"), (LPCTSTR)m_TempName);
			}
			else {
				prompt.Format(_T("Would you like to save '%s'?"), (LPCTSTR)m_Filename);
			}

			int ync = MessageBox(prompt, _T("Save File"), MB_YESNOCANCEL);
			if (ync == IDNO) return true;	// True means anything but IDCANCEL
			else if (ync == IDCANCEL) return false;
		}

		if (m_Filename.IsEmpty()) {
			return SaveAs();
		}
		else {
			if (TextToFile(m_Directory + m_Filename, GetText())) m_CodeEditWnd.SetModified(false);
		}
	}
	return true;
}

bool CDialectView::SaveAs()
{
	static TCHAR filter[] =
		_T("Dialect Files (*.d)\0*.d\0")
		_T("Layout Files (*.lyo)\0*.lyo\0")
		_T("Text Files (*.txt)\0*.txt\0")
		_T("All Files (*.*)\0*.*\0\0");

	TCHAR _FullPath[MAX_PATH];
	memset(_FullPath, 0, MAX_PATH * sizeof(TCHAR));

	OPENFILENAME ofn;
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = GetSafeHwnd();
	ofn.hInstance = AfxGetInstanceHandle();
	ofn.lpstrFilter = filter;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile = _FullPath;
	ofn.nMaxFile = MAX_PATH;
#ifdef _WIN32_WCE_PSPC
	ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
#else
	ofn.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_NOREADONLYRETURN | OFN_HIDEREADONLY;
#endif
	ofn.lpstrDefExt = _T("d");

	if (GetSaveFileName(&ofn)) {
		CString fullPath = _FullPath;//dlg.GetPathName();
#ifdef _WIN32_WCE_PSPC
		// Something strange happening
		if (fullPath.Left(2) == _T("\\\\"))
			fullPath = fullPath.Mid(1);
		if (fullPath[fullPath.GetLength() - 1] == '.')
			fullPath += _T("d");//fullPath = fullPath.Left(fullPath.GetLength() - 1);
#endif
		CString filename;// = dlg.GetFileName();
		CString directory;// = fullPath.Left(fullPath.GetLength() - filename.GetLength());
		SplitFullPath(fullPath, directory, filename);
		//MessageBox(_T("Fullpath: ") + fullPath + _T("\r\nFilename: ") + filename);

		if (TextToFile(fullPath, GetText())) {
			// Remove the old module name before setting file info, which
			// is what the child frame uses to return the module name
			g_ModNameToView.Set(GetModuleName(), AllocNil());
			g_FilenameToView.Set(m_Directory + m_Filename, AllocNil());
			SetFileInfo(directory, filename);
			g_ModNameToView.Set(GetModuleName(), (int)this);
			g_FilenameToView.Set(m_Directory + m_Filename, (int)this);
			m_CodeEditWnd.SetModified(false);
			return true;
		}
	}
	return false;
}

bool CDialectView::TextToFile(CString filename, CString text)
{
	// Filename should contain full path
	CFile file;
	if (!file.Open(filename, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite)) {
		MessageBox(_T("Failed to open file for writing: ") + filename);
		return false;
	}
	else {
		DWORD length = text.GetLength();
		file.Write((LPCTSTR)text, length * sizeof(TCHAR));
		file.Close();
		return true;
	}
}

bool CDialectView::FileToText(CString filename, CString & text)
{
	// Filename should contain full path, data returned in text
	CFile file;
	if (!file.Open(filename, CFile::modeRead | CFile::shareDenyWrite)) {
		MessageBox(_T("Failed to open file for reading: ") + filename);
		return false;
	}

	DWORD length = file.GetLength();
	char* buf = new char[length + 2];	// + 2 in case of Unicode NULL term

	DWORD bytesRead = file.Read(buf, length);
	ASSERT(length == bytesRead);
	buf[length] = buf[length + 1] = 0;	// NULL terminate

	if ((length > 1) && ((buf[0] == 0) || (buf[1] == 0))) {
		text = (wchar_t*)buf;	// Looks like Unicode to me
	}
	else {
		text = buf;	// Looks like ASCII
	}

	delete[] buf;
	file.Close();
	return true;
}

void CDialectView::SetReadOnly(bool bReadOnly)
{
	m_CodeEditWnd.SetReadOnly(bReadOnly);
}

void CDialectView::SetBreakpoint(int line, bool bBreak)
{
	m_CodeEditWnd.SetBreakpoint(line, bBreak);
}

void CDialectView::OnProjectInsertbreakpoint()
{
	m_CodeEditWnd.ToggleBreakpoint(-1);
}

void CDialectView::Print(HDC hDC)
{
#ifndef UNDER_CE
	CDC* pDC = CDC::FromHandle(hDC);

	// Gather some printer info...
	POINT ptGutter, ptPageSize;
	Escape(hDC, GETPRINTINGOFFSET, 0, NULL, &ptGutter);
	Escape(hDC, GETPHYSPAGESIZE, 0, NULL, &ptPageSize);
	int pixPerInchX = GetDeviceCaps(hDC, LOGPIXELSX);
	int pixPerInchY = GetDeviceCaps(hDC, LOGPIXELSY);

	// The margins in inches...
	float left = 0.5f;
	float right = 0.5f;
	float top = 0.5f;
	float bottom = 0.5f;

	// Margins in actual device coordinate system...
	RECT rcMargins;
	rcMargins.left = (int)ceil(left * (float)pixPerInchX) - ptGutter.x;
	rcMargins.top = (int)ceil(top * (float)pixPerInchY) - ptGutter.y;
	rcMargins.right = ptPageSize.x - (int)ceil(right * (float)pixPerInchX) - ptGutter.x;
	rcMargins.bottom = ptPageSize.y - (int)ceil(bottom * (float)pixPerInchY) - ptGutter.y;

	int fontSize = 10;
	CString fontName = _T("Courier New");
	HFONT newFont = CreateFont(
		-MulDiv(fontSize, GetDeviceCaps(hDC, LOGPIXELSY), 72),	// logical height of font
		0, 											// logical average character width
		0,											// angle of escapement
		0,											// base-line orientation angle
		/*fontStyle & FN_BOLD ? FW_BOLD :*/ FW_NORMAL,	// font weight
		/*fontStyle & FN_ITALIC ? TRUE :*/ FALSE,		// italic attribute flag
		/*fontStyle & FN_UNDERLINE ? TRUE :*/ FALSE,	// underline attribute flag
		FALSE,										// strikeout attribute flag
		DEFAULT_CHARSET,							// character set identifier
		OUT_DEFAULT_PRECIS,							// output precision
		CLIP_DEFAULT_PRECIS,						// clipping precision
		PROOF_QUALITY,								// output quality
		DEFAULT_PITCH | FF_DONTCARE,				// pitch and family
		(LPCTSTR)fontName							// pointer to typeface name string
	);

	CFont font;
	if (!font.CreatePointFont(90, _T("Courier New"), pDC)) {
		MessageBox(_T("Failed to create printer font"));
	}

	CString docName = m_Filename;
	if (docName.IsEmpty()) docName = m_TempName;

	DOCINFO di;
	di.cbSize = sizeof(di);
	di.lpszDocName = (LPCTSTR)docName;
	di.lpszOutput = NULL;
	di.lpszDatatype = NULL;
	di.fwType = 0;
	pDC->StartDoc(&di);
	pDC->StartPage();

	// MUST select font into DC after StartPage() under Win95/98
	if (!pDC->SelectObject(font)) {
		MessageBox(_T("Failed to select printer font"));
	}
	// Only support monotype fonts
	TEXTMETRIC tm;
	pDC->GetTextMetrics(&tm);
	int fontWidth = tm.tmAveCharWidth;
	int fontHeight = tm.tmHeight;

	CString header;
	int page = 1;
	int lineCount = m_CodeEditWnd.LineCount();
	int x = rcMargins.left, y = rcMargins.top;
	for (int lineNum = 0; lineNum < lineCount; ++lineNum) {
		if (y + fontHeight > rcMargins.bottom) {
			pDC->EndPage();
			pDC->StartPage();
			pDC->SelectObject(font);	// Required for Win95 (not NT though)
			x = rcMargins.left;
			y = rcMargins.top;
		}

		if (y == rcMargins.top) {
			// Print page header...
			header.Format(_T("File: %s    Page: %d"), docName, page++);
			CSize extent = pDC->GetTextExtent(header);
			x = (ptPageSize.x / 2) -  (extent.cx / 2);
			pDC->TextOut(x, y, header);
			y += fontHeight;
			pDC->MoveTo(x, y);
			pDC->LineTo(x + extent.cx, y);
			y += fontHeight;
			x = rcMargins.left;
		}
		// Notice that we don't currently wrap lines!
		CString line = m_CodeEditWnd.GetLineOfText(lineNum);
		CString toPrint;
		int length = line.GetLength();
		for (int idx = 0; idx < length; ++idx) {
			if (line[idx] == '\t') toPrint += _T("    ");
			else toPrint += line[idx];
		}
		pDC->TextOut(x, y, toPrint);
		y += fontHeight;
	}
	pDC->EndPage();
	pDC->EndDoc();
#endif // !UNDER_CE
}

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////

void CDialectView::OnEditCopy()
{
	m_CodeEditWnd.Copy();
}

void CDialectView::OnUpdateEditCopy(CCmdUI* pCmdUI)
{

}

void CDialectView::OnEditCut()
{
	m_CodeEditWnd.Cut();
}

void CDialectView::OnUpdateEditCut(CCmdUI* pCmdUI)
{

}

void CDialectView::OnEditDelete()
{
	m_CodeEditWnd.Delete();
}

void CDialectView::OnUpdateEditDelete(CCmdUI* pCmdUI)
{

}

void CDialectView::OnEditPaste()
{
	m_CodeEditWnd.Paste();
}

void CDialectView::OnUpdateEditPaste(CCmdUI* pCmdUI)
{

}

void CDialectView::OnEditCommentOut()
{
	m_CodeEditWnd.CommentOut();
}

void CDialectView::OnUpdateEditCommentOut(CCmdUI* pCmdUI)
{

}

void CDialectView::OnEditUncomment()
{
	m_CodeEditWnd.Uncomment();
}

void CDialectView::OnEditIndent()
{
	m_CodeEditWnd.Indent();
}

void CDialectView::OnEditUnindent()
{
	m_CodeEditWnd.Unindent();
}


void CDialectView::OnUpdateEditUncomment(CCmdUI* pCmdUI)
{

}

void CDialectView::OnEditRedo()
{
	m_CodeEditWnd.Redo();
}

void CDialectView::OnUpdateEditRedo(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_CodeEditWnd.CanRedo());
}

void CDialectView::OnEditSelectall()
{
	m_CodeEditWnd.SelectAll();
}

void CDialectView::OnEditUndo()
{
	m_CodeEditWnd.Undo();
}

void CDialectView::OnUpdateEditUndo(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(m_CodeEditWnd.CanUndo());
}

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////

bool CDialectView::Find(CString text, bool bMatchCase)
{
	return m_CodeEditWnd.Find(text, bMatchCase);
}

/*void CDialectView::OnProjectMakeExe()
{
	// We need to request a save first if modified...
	if (m_CodeEditWnd.GetModified()) {
		if (!Save(true)) return;	// User pressed cancel
	}

	CMakeExeDlg dlg;
	dlg.m_InitialModule = m_Filename;
	dlg.m_SourceDir = m_Directory;
	dlg.DoModal();
}*/

/////////////////////////////////////////////////////////////////////
#endif // IDE_SUPPORT


